package coder.nav.service;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.date.DateUtil;
import coder.nav.constants.NavCashGroupConstants;
import coder.nav.convert.NavCashConvert;
import coder.nav.enums.NavCashChannelEnums;
import coder.nav.enums.NavCashFlagEnums;
import coder.nav.enums.NavCashTypeEnums;
import coder.nav.enums.NavSettingEnums;
import coder.nav.mapper.NavCashMapper;
import coder.nav.model.NavCash;
import coder.nav.model.bo.NavCashQueryBO;
import coder.nav.model.bo.NavSaveCashBO;
import coder.nav.model.vo.*;
import coder.nav.model.vo.setting.NavCashSettingVO;
import coder.nav.utils.JwtUtils;
import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.apache.commons.lang3.time.DateUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.stream.Collectors;

@Service
public class NavCashService extends ServiceImpl<NavCashMapper, NavCash> {

    @Autowired
    private NavSettingService settingService;

    /**
     * 保存记账
     * @param bo 入参
     * @return 操作数量
     */
    public int saveCash(NavSaveCashBO bo) {
        NavCash entity = BeanUtil.copyProperties(bo, NavCash.class);
        // if(StringUtils.isBlank(entity.getTitle())) {
            entity.setTitle(NavCashTypeEnums.getValueByKey(entity.getType()));
        // }
        entity.setUserId(JwtUtils.getUserId());
        entity.setCreateDate(DateUtil.parseDate(entity.getYear() + "-" + entity.getMonth() + "-" + entity.getDay()));
        if(Objects.isNull(entity.getId())) {
            // 新增
            this.updateCash(entity);
            return this.baseMapper.insert(entity);
        }
        NavCashVO origin = this.getInfo(entity.getId());
        if(!Objects.equals(origin.getFlag(), bo.getFlag())) {
            this.deleteCash(origin.getId());
            bo.setId(null);
            return this.saveCash(bo);
        }
        // 编辑
        int res = this.baseMapper.updateById(entity);
        // 计算余额变化
        entity.setMoney(entity.getMoney().subtract(origin.getMoney()));
        this.updateCash(entity);
        return res;
    }

    /**
     * 删除记账
     * @param id 主键id
     * @return 操作数量
     */
    public int deleteCash(Long id) {
        LambdaQueryWrapper<NavCash> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(NavCash::getUserId, JwtUtils.getUserId());
        queryWrapper.eq(NavCash::getId, id);
        NavCash entity = this.baseMapper.selectOne(queryWrapper);
        entity.setMoney(entity.getMoney().multiply(new BigDecimal(-1)));
        this.updateCash(entity);
        return this.baseMapper.delete(queryWrapper);
    }

    /**
     * 获取记账详情
     * @param id 主键id
     */
    public NavCashVO getInfo(Long id) {
        LambdaQueryWrapper<NavCash> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(NavCash::getUserId, JwtUtils.getUserId());
        queryWrapper.eq(NavCash::getId, id);
        NavCash entity = this.baseMapper.selectOne(queryWrapper);
        return BeanUtil.copyProperties(entity, NavCashVO.class);
    }

    /**
     * 获取余额
     */
    public BigDecimal getCash() {
        return this.getCash(null);
    }

    /**
     * 获取余额
     * @param userId 用户id
     */
    public BigDecimal getCash(Long userId) {
        if(Objects.isNull(userId)) {
            userId = JwtUtils.getUserId();
        }
        NavCashSettingVO setting = settingService.getCashSetting(userId);
        if(Objects.isNull(setting)) {
            return BigDecimal.ZERO;
        }
        return setting.getCash();
    }

    /**
     * 总支出/收入
     * @param bo 入参
     */
    public BigDecimal getSum(NavCashQueryBO bo) {
        return this.getSum(bo, null);
    }

    /**
     * 总支出/收入
     * @param bo 入参
     * @param userId 用户id
     */
    public BigDecimal getSum(NavCashQueryBO bo, Long userId) {
        if(Objects.isNull(userId)) {
            userId = JwtUtils.getUserId();
        }
        QueryWrapper<NavCash> queryWrapper = new QueryWrapper<>();
        queryWrapper.select("sum(money) as money");
        LambdaQueryWrapper<NavCash> lambdaQueryWrapper = NavCashConvert.WrapperBuilder.build(bo, queryWrapper, userId);
        NavCash entity = this.baseMapper.selectOne(lambdaQueryWrapper);
        if(Objects.nonNull(entity)) {
            return entity.getMoney();
        }
        return BigDecimal.ZERO;
    }

    /**
     * 日均
     * @param bo 入参
     */
    public BigDecimal getAvg(NavCashQueryBO bo) {
        return this.getAvg(bo, null);
    }

    /**
     * 日均
     * @param bo 入参
     * @param userId 用户id
     */
    public BigDecimal getAvg(NavCashQueryBO bo, Long userId) {
        if(Objects.isNull(userId)) {
            userId = JwtUtils.getUserId();
        }
        bo.setFlags(Arrays.asList(NavCashFlagEnums.SUB.getKey()));
        BigDecimal sum = this.getSum(bo, userId);
        Date startDate = bo.getStartDate();
        Date endDate = bo.getEndDate();
        // 获取用户最早的消费日期
        LambdaQueryWrapper<NavCash> firstQueryWrapper = NavCashConvert.WrapperBuilder.build(bo, null, userId, false);
        firstQueryWrapper.orderByAsc(NavCash::getCreateDate);
        firstQueryWrapper.orderByAsc(NavCash::getId);
        firstQueryWrapper.last(" limit 1 ");
        NavCash firstCash = this.baseMapper.selectOne(firstQueryWrapper);
        if(Objects.isNull(firstCash)) {
            // 不存在消费记录
            return BigDecimal.ZERO;
        }
        Date firstDate = firstCash.getCreateDate();
        if(Objects.isNull(startDate) || startDate.before(firstDate)) {
            startDate = firstDate;
        }
        Date curDate = new Date();
        if(Objects.isNull(endDate) || endDate.after(curDate)) {
            endDate = curDate;
        }
        // 获取用户最后消费日期
        LambdaQueryWrapper<NavCash> lastQueryWrapper = NavCashConvert.WrapperBuilder.build(bo, null, userId, false);
        lastQueryWrapper.orderByDesc(NavCash::getCreateDate);
        lastQueryWrapper.orderByDesc(NavCash::getId);
        lastQueryWrapper.last(" limit 1 ");
        NavCash lastCash = this.baseMapper.selectOne(lastQueryWrapper);
        if(Objects.isNull(lastCash)) {
            // 不存在消费记录
            return BigDecimal.ZERO;
        }
        Date lastDate = lastCash.getCreateDate();
        if(endDate.after(lastDate)) {
            endDate = lastDate;
        }
        long num = DateUtil.betweenDay(startDate, endDate, false) + 1;
        return sum.divide(new BigDecimal(num), RoundingMode.CEILING);
    }

    /**
     * 修改余额
     * @param entity 实体
     */
    public void updateCash(NavCash entity) {
        BigDecimal change = entity.getMoney();
        Integer flag = entity.getFlag();
        if(Objects.equals(NavCashFlagEnums.SUB.getKey(), flag)) {
            change = change.multiply(new BigDecimal(-1));
        }
        BigDecimal cash = this.getCash();
        cash = cash.add(change);
        // 修改
        NavCashSettingVO setting = settingService.getCashSetting();
        setting.setCash(cash);
        settingService.saveSetting(NavSettingEnums.CASH.getKey(), JSON.toJSONString(setting), false);
    }

    /**
     * 列表
     * @param bo 入参
     * @param userId 用户id
     */
    public List<NavCashVO> getCashList(NavCashQueryBO bo, Long userId) {
        if(Objects.isNull(userId)) {
            userId = JwtUtils.getUserId();
        }
        LambdaQueryWrapper<NavCash> queryWrapper = NavCashConvert.WrapperBuilder.build(bo, null, userId);
        List<NavCash> list = this.baseMapper.selectList(queryWrapper);
        return list.stream().map(item -> BeanUtil.copyProperties(item, NavCashVO.class)).collect(Collectors.toList());
    }

    /**
     * 分页
     * @param bo 入参
     */
    public NavPageVO<NavCashVO> getCashPage(NavCashQueryBO bo) {
        Long userId = JwtUtils.getUserId();
        LambdaQueryWrapper<NavCash> queryWrapper = NavCashConvert.WrapperBuilder.build(bo, null, userId);
        Page<NavCash> limit = new Page<>(bo.getPageIndex(), bo.getPageSize(), true);
        Page<NavCash> page = this.baseMapper.selectPage(limit, queryWrapper);
        NavPageVO<NavCashVO> vo = new NavPageVO<>(bo.getPageIndex(), bo.getPageSize(), page.getTotal());
        vo.setData(page.getRecords().stream().map(item -> BeanUtil.copyProperties(item, NavCashVO.class)).collect(Collectors.toList()));
        return vo;
    }

    /**
     * 支付渠道
     */
    public List<NavKeyValueVO<Integer, String>> getChannelList() {
        return NavCashChannelEnums.getList();
    }

    /**
     * 支出/收入
     */
    public List<NavKeyValueVO<Integer, String>> getFlagList() {
        return NavCashFlagEnums.getList();
    }

    /**
     * 消费类型
     */
    public List<NavKeyValueVO<Integer, String>> getTypeList() {
        return NavCashTypeEnums.getList();
    }

    public LambdaQueryWrapper<NavCash> getChartQueryWrapper(NavCashQueryBO bo, Long userId) {
        if(Objects.isNull(userId)) {
            userId = JwtUtils.getUserId();
        }
        LambdaQueryWrapper<NavCash> queryWrapper = NavCashConvert.WrapperBuilder.build(bo, null, userId);
        if(Objects.isNull(bo.getStartDate()) && Objects.isNull(bo.getEndDate())) {
            Date endDate = new Date();
            // 收支-年
            if(NavCashGroupConstants.YEAR.equals(bo.getGroupBy())) {
                Date startDate = DateUtils.addYears(endDate, -1);
                queryWrapper.ge(NavCash::getCreateDate, startDate);
                queryWrapper.le(NavCash::getCreateDate, endDate);
            }
            // 收支-月
            if(NavCashGroupConstants.MONTH.equals(bo.getGroupBy())) {
                Date startDate = DateUtils.addMonths(endDate, -1);
                queryWrapper.ge(NavCash::getCreateDate, startDate);
                queryWrapper.le(NavCash::getCreateDate, endDate);
            }
        }
        return queryWrapper;
    }

    /**
     * 图标数据处理
     * @param entity 实体
     * @param groupBy 分组维度
     */
    public String getChartDataKey(NavCash entity, String groupBy) {
        String key = "";
        // 收支-年
        if(NavCashGroupConstants.YEAR.equals(groupBy)) {
            SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM");
            key = sdf.format(entity.getCreateDate());
        }
        // 收支-月
        if(NavCashGroupConstants.MONTH.equals(groupBy)) {
            SimpleDateFormat sdf = new SimpleDateFormat("MM-dd");
            key = sdf.format(entity.getCreateDate());
        }
        // 支出-标题
        if(NavCashGroupConstants.TITLE.equals(groupBy)) {
            key = entity.getTitle();
        }
        // 支出-类型
        if(NavCashGroupConstants.TYPE.equals(groupBy)) {
            key = NavCashTypeEnums.getValueByKey(entity.getType());
        }
        // 支出-渠道
        if(NavCashGroupConstants.CHANNEL.equals(groupBy)) {
            key = NavCashChannelEnums.getValueByKey(entity.getChannel());
        }
        return key;
    }

    /**
     * 图标数据处理
     * @param data 最终返回数据
     * @param list 数据库数据
     * @param flag 收支类型
     * @param groupBy 分组维度
     */
    public Map<String, Map<String, BigDecimal>> dealChartBarData(Map<String, Map<String, BigDecimal>> data, List<NavCash> list, NavCashFlagEnums flag, String groupBy) {
        list.forEach(item -> {
            String key = this.getChartDataKey(item, groupBy);
            Map<String, BigDecimal> map = data.getOrDefault(key, new HashMap<>());
            BigDecimal change = map.getOrDefault(flag.getValue(), BigDecimal.ZERO);
            change = change.add(item.getMoney());
            if(NavCashFlagEnums.ADD == flag) {
                map.put(NavCashFlagEnums.ADD.getValue(), change);
                map.put(NavCashFlagEnums.SUB.getValue(), map.getOrDefault(NavCashFlagEnums.SUB.getValue(), BigDecimal.ZERO));
            }
            if(NavCashFlagEnums.SUB == flag) {
                map.put(NavCashFlagEnums.ADD.getValue(), map.getOrDefault(NavCashFlagEnums.ADD.getValue(), BigDecimal.ZERO));
                map.put(NavCashFlagEnums.SUB.getValue(), change);
            }
            data.put(key, map);
        });
        return data;
    }

    /**
     * 柱状图-数据
     * @param bo 入参
     */
    public NavChartBarVO getChartBar(NavCashQueryBO bo) {
        return this.getChartBar(bo, null);
    }

    /**
     * 柱状图-数据
     * @param bo 入参
     * @param userId 用户id
     */
    public NavChartBarVO getChartBar(NavCashQueryBO bo, Long userId) {
        LambdaQueryWrapper<NavCash> queryWrapper = this.getChartQueryWrapper(bo, userId);
        Set<String> dimensions = new TreeSet<>(String::compareTo);
        Map<String, Map<String, BigDecimal>> data = new TreeMap<>(String::compareTo);
        // 收入
        LambdaQueryWrapper<NavCash> addQueryWrapper = queryWrapper.clone();
        addQueryWrapper.eq(NavCash::getFlag, NavCashFlagEnums.ADD.getKey());
        List<NavCash> addList = this.baseMapper.selectList(addQueryWrapper);
        data = this.dealChartBarData(data, addList, NavCashFlagEnums.ADD, bo.getGroupBy());
        // 支出
        LambdaQueryWrapper<NavCash> subQueryWrapper = queryWrapper.clone();
        subQueryWrapper.eq(NavCash::getFlag, NavCashFlagEnums.SUB.getKey());
        List<NavCash> subList = this.baseMapper.selectList(subQueryWrapper);
        data = this.dealChartBarData(data, subList, NavCashFlagEnums.SUB, bo.getGroupBy());
        // 整合
        dimensions.add(NavCashFlagEnums.ADD.getValue());
        dimensions.add(NavCashFlagEnums.SUB.getValue());
        return new NavChartBarVO(dimensions, data);
    }

    /**
     * 线型图-数据
     * @param bo 入参
     */
    public NavChartLineVO getChartLine(NavCashQueryBO bo) {
        return this.getChartLine(bo, null);
    }

    /**
     * 线型图-数据
     * @param bo 入参
     * @param userId 用户id
     */
    public NavChartLineVO getChartLine(NavCashQueryBO bo, Long userId) {
        LambdaQueryWrapper<NavCash> queryWrapper = this.getChartQueryWrapper(bo, userId);
        Set<String> legend = new TreeSet<>(String::compareTo);
        Map<String, Map<String, BigDecimal>> data = new TreeMap<>(String::compareTo);
        // 收入
        LambdaQueryWrapper<NavCash> addQueryWrapper = queryWrapper.clone();
        addQueryWrapper.eq(NavCash::getFlag, NavCashFlagEnums.ADD.getKey());
        List<NavCash> addList = this.baseMapper.selectList(addQueryWrapper);
        data = this.dealChartBarData(data, addList, NavCashFlagEnums.ADD, bo.getGroupBy());
        // 支出
        LambdaQueryWrapper<NavCash> subQueryWrapper = queryWrapper.clone();
        subQueryWrapper.eq(NavCash::getFlag, NavCashFlagEnums.SUB.getKey());
        List<NavCash> subList = this.baseMapper.selectList(subQueryWrapper);
        data = this.dealChartBarData(data, subList, NavCashFlagEnums.SUB, bo.getGroupBy());
        // 整合
        legend.add(NavCashFlagEnums.ADD.getValue());
        legend.add(NavCashFlagEnums.SUB.getValue());
        return new NavChartLineVO(legend, data);
    }

    /**
     * 饼状图-数据
     * @param bo 入参
     */
    public NavChartPieVO getChartPie(NavCashQueryBO bo) {
        return this.getChartPie(bo, null);
    }

    /**
     * 饼状图-数据
     * @param bo 入参
     * @param userId 用户id
     */
    public NavChartPieVO getChartPie(NavCashQueryBO bo, Long userId) {
        LambdaQueryWrapper<NavCash> queryWrapper = this.getChartQueryWrapper(bo, userId);
        queryWrapper.eq(NavCash::getFlag, NavCashFlagEnums.SUB.getKey());
        Map<String, BigDecimal> data = new TreeMap<>(String::compareTo);
        // 支出
        List<NavCash> list = this.baseMapper.selectList(queryWrapper);
        // 整合
        list.forEach(item -> {
            String key = this.getChartDataKey(item, bo.getGroupBy());
            BigDecimal count = data.getOrDefault(key, BigDecimal.ZERO);
            count = count.add(item.getMoney());
            data.put(key, count);
        });
        return new NavChartPieVO(data);
    }

    /**
     * 饼状图-收支
     * @param bo 入参
     */
    public NavChartPieVO getChartPieSum(NavCashQueryBO bo) {
        return this.getChartPieSum(bo, null);
    }

    /**
     * 饼状图-收支
     * @param bo 入参
     * @param userId 用户id
     */
    public NavChartPieVO getChartPieSum(NavCashQueryBO bo, Long userId) {
        Map<String, BigDecimal> data = new TreeMap<>();
        // 收入
        NavCashQueryBO addBO = BeanUtil.copyProperties(bo, NavCashQueryBO.class);
        addBO.setFlags(Arrays.asList(NavCashFlagEnums.ADD.getKey()));
        BigDecimal addSum = this.getSum(addBO, userId);
        // 支出
        NavCashQueryBO subBO = BeanUtil.copyProperties(bo, NavCashQueryBO.class);
        subBO.setFlags(Arrays.asList(NavCashFlagEnums.SUB.getKey()));
        BigDecimal subSum = this.getSum(subBO, userId);

        data.put(NavCashFlagEnums.ADD.getValue(), addSum);
        data.put(NavCashFlagEnums.SUB.getValue(), subSum);

        return new NavChartPieVO(data);
    }

    /**
     * 雷达图-数据
     * @param bo 入参
     */
    public NavChartRadarVO getChartRadar(NavCashQueryBO bo) {
        return this.getChartRadar(bo, null);
    }

    /**
     * 雷达图-数据
     * @param bo 入参
     * @param userId 用户id
     */
    public NavChartRadarVO getChartRadar(NavCashQueryBO bo, Long userId) {
        LambdaQueryWrapper<NavCash> queryWrapper = this.getChartQueryWrapper(bo, userId);
        Set<String> legend = new TreeSet<>(String::compareTo);
        Map<String, Map<String, BigDecimal>> data = new TreeMap<>(String::compareTo);
        // 收入
        LambdaQueryWrapper<NavCash> addQueryWrapper = queryWrapper.clone();
        addQueryWrapper.eq(NavCash::getFlag, NavCashFlagEnums.ADD.getKey());
        List<NavCash> addList = this.baseMapper.selectList(addQueryWrapper);
        data = this.dealChartBarData(data, addList, NavCashFlagEnums.ADD, bo.getGroupBy());
        // 支出
        LambdaQueryWrapper<NavCash> subQueryWrapper = queryWrapper.clone();
        subQueryWrapper.eq(NavCash::getFlag, NavCashFlagEnums.SUB.getKey());
        List<NavCash> subList = this.baseMapper.selectList(subQueryWrapper);
        data = this.dealChartBarData(data, subList, NavCashFlagEnums.SUB, bo.getGroupBy());
        // 整合
        legend.add(NavCashFlagEnums.ADD.getValue());
        legend.add(NavCashFlagEnums.SUB.getValue());
        return new NavChartRadarVO(legend, data);
    }

}
